/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * <http://www.mongodb.com/licensing/server-side-public-license>.
 */
import Reflux from 'reflux';

import UserNotification from 'util/UserNotification';
import * as URLUtils from 'util/URLUtils';
import PaginationURL from 'util/PaginationURL';
import fetch from 'logic/rest/FetchProvider';
import { singletonStore, singletonActions } from 'logic/singleton';
import type { LookupTableCache } from 'logic/lookup-tables/types';

type Actions = {
  searchPaginated: (page: number, perPage: number, query?: string) => Promise<StoreState>;
  reloadPage: () => Promise<void>;
  get: (idOrName: string) => Promise<LookupTableCache>;
  create: (cache: LookupTableCache) => Promise<void>;
  update: (cache: LookupTableCache) => Promise<void>;
  getTypes: () => Promise<unknown>;
  delete: (idOrName: string) => Promise<void>;
  validate: (cache: LookupTableCache) => Promise<unknown>;
};
export const LookupTableCachesActions = singletonActions('core.LookupTableCaches', () =>
  Reflux.createActions<Actions>({
    searchPaginated: { asyncResult: true },
    reloadPage: { asyncResult: true },
    get: { asyncResult: true },
    create: { asyncResult: true },
    delete: { asyncResult: true },
    update: { asyncResult: true },
    getTypes: { asyncResult: true },
    validate: { asyncResult: true },
  }),
);

type StoreState = {
  caches: LookupTableCache[];
  types: any;
  pagination: {
    page: number;
    per_page: number;
    total: number;
    count: number;
    query: string | null;
  };
  validationErrors: any;
};
export const LookupTableCachesStore = singletonStore('core.LookupTableCaches', () =>
  Reflux.createStore<StoreState>({
    listenables: [LookupTableCachesActions],
    cache: null,
    caches: null,
    types: null,
    pagination: {
      page: 1,
      per_page: 10,
      total: 0,
      count: 0,
      query: null,
    },
    validationErrors: {},

    getInitialState() {
      return this.getState();
    },

    getState() {
      return {
        cache: this.cache,
        caches: this.caches,
        types: this.types,
        pagination: this.pagination,
        validationErrors: this.validationErrors,
      };
    },

    propagateChanges() {
      this.trigger(this.getState());
    },

    reloadPage() {
      const promise = this.searchPaginated(this.pagination.page, this.pagination.per_page, this.pagination.query);

      LookupTableCachesActions.reloadPage.promise(promise);

      return promise;
    },

    searchPaginated(page, perPage, query, sort?: string, order?: 'asc' | 'desc') {
      const url = this._url(PaginationURL('caches', page, perPage, query, { sort, order }));

      const promise = fetch('GET', url);

      promise.then(
        (response) => {
          this.pagination = {
            count: response.count,
            total: response.total,
            page: response.page,
            per_page: response.per_page,
            query: response.query,
          };

          this.caches = response.caches;
          this.propagateChanges();
        },
        this._errorHandler('Fetching lookup table caches failed', 'Could not retrieve the lookup caches'),
      );

      LookupTableCachesActions.searchPaginated.promise(promise);

      return promise;
    },

    get(idOrName) {
      const url = this._url(`caches/${idOrName}`);
      const promise = fetch('GET', url);

      promise.then(
        (response) => {
          this.cache = response;
          this.propagateChanges();
        },
        this._errorHandler(`Fetching lookup table cache ${idOrName} failed`, 'Could not retrieve lookup table cache'),
      );

      LookupTableCachesActions.get.promise(promise);

      return promise;
    },

    create(cache) {
      const url = this._url('caches');
      const promise = fetch('POST', url, cache);

      promise.then(
        (response) => {
          this.cache = response;
          this.propagateChanges();
        },
        this._errorHandler('Creating lookup table cache failed', `Could not create lookup table cache "${cache.name}"`),
      );

      LookupTableCachesActions.create.promise(promise);

      return promise;
    },

    update(cache) {
      const url = this._url(`caches/${cache.id}`);
      const promise = fetch('PUT', url, cache);

      promise.then(
        (response) => {
          this.cache = response;
          this.propagateChanges();
        },
        this._errorHandler('Updating lookup table cache failed', `Could not update lookup table cache "${cache.name}"`),
      );

      LookupTableCachesActions.update.promise(promise);

      return promise;
    },

    getTypes() {
      const url = this._url('types/caches');
      const promise = fetch('GET', url);

      promise.then(
        (response) => {
          this.types = response;
          this.propagateChanges();
        },
        this._errorHandler('Fetching available types failed', 'Could not fetch the available lookup table cache types'),
      );

      LookupTableCachesActions.getTypes.promise(promise);

      return promise;
    },

    delete(idOrName) {
      const url = this._url(`caches/${idOrName}`);
      const promise = fetch('DELETE', url);

      promise.catch(
        this._errorHandler('Deleting lookup table cache failed', `Could not delete lookup table cache "${idOrName}"`),
      );

      LookupTableCachesActions.delete.promise(promise);

      return promise;
    },

    validate(cache) {
      const url = this._url('caches/validate');
      const promise = fetch('POST', url, cache);

      promise.then(
        (response) => {
          this.validationErrors = response.errors;
          this.propagateChanges();
        },
        this._errorHandler(
          'Lookup table cache validation failed',
          `Could not validate lookup table cache "${cache.name}"`,
        ),
      );

      LookupTableCachesActions.validate.promise(promise);

      return promise;
    },

    _errorHandler(message, title, cb) {
      return (error) => {
        let errorMessage;

        try {
          errorMessage = error.additional.body.message;
        } catch (_e) {
          errorMessage = error.message;
        }

        UserNotification.error(`${message}: ${errorMessage}`, title);

        if (cb) {
          cb(error);
        }
      };
    },

    _url(path) {
      return URLUtils.qualifyUrl(`/system/lookup/${path}`);
    },
  }),
);
